package com.lhl.jwt.utils;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.lhl.jwt.domain.dto.PayloadDTO;
import com.lhl.jwt.exception.JwtExpireException;
import com.lhl.jwt.exception.JwtSignatureVerifyException;
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;

import java.text.ParseException;
import java.time.Instant;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * jwt 工具类
 *
 */
@Slf4j
public class JwtUtils {
	// 默认加密数据,一般采用动态的用户名加密
	public static final String  TOKEN_SECRET          = "king";
	// 过期时间2个小时
	private static final long	EXPIRATION			= 120L;
	public static String createToken(String username, Collection<? extends GrantedAuthority> authorities, String secret) throws JOSEException {
		List<String> as = authorities.stream()
				.map((auth) -> auth.getAuthority())
				.collect(Collectors.toList());
		PayloadDTO payloadDTO = getDefaultPayloadDTO();
		payloadDTO.setSub(username);
		payloadDTO.setUsername(username);
		payloadDTO.setAuthorities(as);
		// 生成token
		String payloadJsonString = JSONUtil.toJsonStr(payloadDTO);

		return createToken(payloadJsonString, secret);
	}

	public static String createToken(String payloadJsonString, String secret) throws JOSEException {
		// 创建Header(设置以JWSAlgorithm.HS256算法进行签名认证)
		JWSHeader jwsHeader = new JWSHeader(JWSAlgorithm.HS256);
		// 建立Payload
		Payload payload = new Payload(payloadJsonString);

		/// Signature相关
		// 根据Header以及Payload创建JSON Web Signature (JWS)对象
		JWSObject jwsObject = new JWSObject(jwsHeader, payload);
		// 使用给的对称加密密钥，创建一个加密器
		JWSSigner jwsSigner = new MACSigner(SecureUtil.md5(TOKEN_SECRET+secret));
		// 将该签名器 与 JSON Web Signature (JWS)对象进行关联
		// 即: 指定JWS使用该签名器进行签名
		jwsObject.sign(jwsSigner);

		// 使用JWS生成JWT(即:使用JWS生成token)
		return jwsObject.serialize();
	}
	public static PayloadDTO getPayLoadByToken(String token, String secret) throws ParseException, JOSEException {
		// 解析token，将token转换为JWSObject对象
		JWSObject jwsObject = JWSObject.parse(token);

		// 创建一个JSON Web Signature (JWS) verifier.用于校验签名(即:校验token是否被篡改)
		JWSVerifier jwsVerifier = new MACVerifier(SecureUtil.md5(TOKEN_SECRET+secret));
		// 如果校验到token被篡改(即:签名认证失败)，那么抛出异常
		if(!jwsObject.verify(jwsVerifier)) {
			throw new JwtSignatureVerifyException("签名认证失败!");
		}
		String payload = jwsObject.getPayload().toString();
		PayloadDTO payloadDto = JSONUtil.toBean(payload, PayloadDTO.class);
		if (payloadDto.getExp()< Instant.now().getEpochSecond()) {
			throw new JwtExpireException("token已过期！");
		}
		return payloadDto;
	}

	/**
	 * @param: sub
	 * @description: 获得初始PayloadDTO数据
	 * @return: com.lhl.jwt.domain.dto.PayloadDTO
	 * @author: king
	 * @date: 2021-01-11 10:50
	 */
	public static PayloadDTO getDefaultPayloadDTO(){
		Instant now = Instant.now();
		Instant exp = now.plusSeconds(EXPIRATION);
		log.debug("now=={}", LocalDateTimeUtil.of(now));
		log.debug("now=={}", now.toEpochMilli());
		return PayloadDTO.builder()
				.iat(now.getEpochSecond())
				.exp(exp.getEpochSecond())
				.jti(UUID.randomUUID().toString())
				.build();

	}
}
